﻿#region Imports
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CommonUtilities.Enums;
using CommonUtilities.Extensions;
using CommonUtilities.Info;
using CommonUtilities.Messages;
using CommonUtilities.Models;
using CommonUtilities.Util;
using PlaylistAnalyzer.Models;
using PlaylistAnalyzer.ResourceFiles;
#endregion Imports

namespace PlaylistAnalyzer.Views
{
    public partial class frmPlaylistAnalyzer : Form
    {
        public void PopulateDummyData()
        {
            AllPlaylistTypes = AllPlaylistTypes
                .AddItem(new PlaylistType()
                   {
                       PlaylistName = "WMPPL",
                       PlaylistExtn = "*.wpl",
                       PlaylistDescription = "Windows Media playlist"
                   })
                .AddItem(new PlaylistType()
                   {
                       PlaylistName = "MPCPL",
                       PlaylistExtn = "*.mpcpl",
                       PlaylistDescription = "Media Player Classic playlist"
                   }).ToObservableCollection();

            SelectedPlaylistType = AllPlaylistTypes.First();
        }

        #region Declarations
        private bool hasSeqStarted = false;
        private const string sNoActivity = "No activity";
        #endregion Declarations

        #region Properties
        public ObservableCollection<FileDetails> AllFileDetails { get; private set; }

        public PlaylistType SelectedPlaylistType { get; private set; }

        public ObservableCollection<PlaylistType> AllPlaylistTypes { get; private set; }

        private string targetDirectory = "";
        public string TargetDirectory
        {
            get { return targetDirectory; }
            private set
            {
                targetDirectory = value;
                EvaluateCopyButton();

                tltipMain.SetToolTip(txtTargetPath,
                    ResourcePlaylistAnalyzer.Tooltip_TargetDirectory.FormatTextFromParams(Path.GetFileName(targetDirectory)));
            }
        }

        public ObservableCollection<FileCopyError> AllCopyErrors { get; private set; }

        private string playlistFile = "";
        public string PlaylistFile
        {
            get { return playlistFile; }
            private set
            {
                playlistFile = value;
                EvaluateLoadButton();

                PlaylistType plType = AllPlaylistTypes.Where(pltyp => pltyp.PlaylistExtn.RemoveExtnChars() == Path.GetExtension(playlistFile).RemoveExtnChars()).FirstOrDefault();
                string sPlaylistType = "{0} ({1})".FormatTextFromParams(plType.PlaylistDescription, plType.PlaylistExtn.AddExtnChars());
                tltipMain.SetToolTip(txtPlaylistPath,
                    ResourcePlaylistAnalyzer.Tooltip_PlaylistFile.FormatTextFromParams(Path.GetFileName(playlistFile), sPlaylistType));
            }
        }
        #endregion Properties

        #region Constructors
        public frmPlaylistAnalyzer()
        {
            InitializeComponent();
            this.Text = AppInfo.ApplicationTitle;
        }
        #endregion Constructors

        #region Events
        private void frmPlaylistAnalyzer_Load(object sender, EventArgs e)
        {
            AllPlaylistTypes = new ObservableCollection<PlaylistType>();
            AllFileDetails = new ObservableCollection<FileDetails>();
            PopulateDummyData();

            EvaluateLoadButton();
            EvaluateCopyButton();
            mnuDelete.Enabled = btnDelete.Enabled = false;
            chkAutoLoad.Checked = true;
            //chkAutoLoad.Enabled = false;

            stsCurrentStatus.Text = sNoActivity;
            stsProgress.Value = 0;
        }

        private void frmPlaylistAnalyzer_Resize(object sender, EventArgs e)
        {
            btnDelete.Location = new Point(btnDelete.Location.X, (lstSongs.Bottom - 5) - btnDelete.Height);
            stsCurrentStatus.Width = this.Width - stsProgress.Width - 30;
        }

        private void btnBrowsePlaylist_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlgOpenFile = new OpenFileDialog()
            {
                AddExtension = false,
                CheckPathExists = true,
                CheckFileExists = true,
                DefaultExt = AllPlaylistTypes.First().PlaylistExtn.RemoveExtnChars(),
                Filter = FormatFilterNames(AllPlaylistTypes),
                InitialDirectory = PlaylistFile.ValidatePath(ePathValidation.File)
                    ? PlaylistFile : GetPlaylistFolder(),
                FileName = PlaylistFile.ValidatePath(ePathValidation.File) ? PlaylistFile : "",
                Multiselect = false,
                ReadOnlyChecked = true,
                RestoreDirectory = true,
                ShowReadOnly = true,
                SupportMultiDottedExtensions = false,
                Title = "Select playlist..",
                ValidateNames = true
            };

            if (dlgOpenFile.ShowDialog(this) == DialogResult.OK)
            {
                txtPlaylistPath.Text = dlgOpenFile.FileName;
                if (chkAutoLoad.Checked) btnLoad_Click(sender, e);
            }
            else
                EvaluateLoadButton();
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            SynchMultimedIntegLang res;
            string sXML = "";
            if (!PlaylistFile.ValidatePath(ePathValidation.File)) return;

            /*
            ObservableCollection<Media> z = new ObservableCollection<Media>();
            res = new SMIL
            {
                Head = new Head
                {
                    Author = "rkd",
                    Title = "sample wpl"
                    //    meta = new meta
                    //    {
                    //        name = "a",
                    //        content = "b"
                    //    }
                },
                Body = new Body
                {
                    Seq = new Seq
                    {
                        AllMedia = new ObservableCollection<Media>(z
                            .AddNewItem(new Media
                            {
                                FilePath = "abc",
                                CId = "{a}",
                                TId = "{a}"
                            })
                            .AddNewItem(new Media
                            {
                                FilePath = "def",
                                CId = "{d}",
                                TId = "{d}"
                            }))
                    }
                }
            };
            */

            sXML = File.ReadAllText(PlaylistFile, Encoding.UTF8);
            sXML = sXML.ConvertStringToLines().ToList().ConvertAll(val => val = XmlCleanup(val)).ToArray().ConvertLinesToString();
            res = sXML.DeSerializeFromFormattedData<SynchMultimedIntegLang>(eSerializationFormat.Xml);

            AllFileDetails.Clear();
            AllFileDetails = AllFileDetails.AddItems(res.Body.Seq.AllMedia
                .Select(val => new
                    {
                        _FileName = Path.GetFileName(val.FilePath),
                        _FileExtn = Path.GetExtension(val.FilePath),
                        _FullPath = val.FilePath
                    }).Distinct()
                .Select(val => new FileDetails
                    {
                        FileName = val._FileName,
                        FileExtn = val._FileExtn,
                        FullPath = val._FullPath,
                        Mp3Tags = MusicID3Tag.ReadFile(val._FullPath)
                    })
                .Where(val => val.FullPath.ValidatePath(ePathValidation.File))
                .OrderBy(val => val.Mp3Tags.Title.Trim()).ThenBy(val => val.FileName)).ToObservableCollection();

            UpdateList(true);

            EvaluateCopyButton();
            MessageBoxUtil.Show(Program.IsDebug, (AllFileDetails.IsLinqIterable(true) ? AllFileDetails.Count.ToString() + " ~ successfully" : "No ~").Replace("~", "file" + (AllFileDetails.HasCount(0, eComparers.GreaterThan) || !AllFileDetails.IsLinqIterable(true) ? "s" : "") + " loaded") + "\t", "File load summary..", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void lstSongs_SelectedValueChanged(object sender, EventArgs e)
        {
            EvaluateDeleteButton();
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            if (lstSongs.SelectedItems.Count > 0)
            {
                ObservableCollection<int> selectedFileIds = lstSongs.SelectedItems.Cast<FileDetails>().Select(fdet => fdet.FileId).ToObservableCollection();
                AllFileDetails = AllFileDetails.RemoveItems(AllFileDetails.Where(fdet => selectedFileIds.Contains(fdet.FileId))).ToObservableCollection();

                UpdateList(false);
            }
        }

        private void btnBrowseTarget_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dlgFolderBrowser = new FolderBrowserDialog()
            {
                Description = "Select target path..",
                ShowNewFolderButton = true
            };

            if (String.IsNullOrEmpty(TargetDirectory))
                dlgFolderBrowser.RootFolder = Environment.SpecialFolder.Desktop;
            else dlgFolderBrowser.SelectedPath = TargetDirectory;

            if (dlgFolderBrowser.ShowDialog(this) == DialogResult.OK)
                txtTargetPath.Text = dlgFolderBrowser.SelectedPath;
        }

        private void btnCopy_Click(object sender, EventArgs e)
        {
            stsProgress.Value = 0;
            if (!AllFileDetails.IsLinqIterable(true)) return;
            if (MessageBoxUtil.Show(Program.IsDebug, "Do you want to proceed with file copy?\t", "File copy confirmation..", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) return;

            frmCopyModeSelection frmCopyMethodSelection = new frmCopyModeSelection();
            if (frmCopyMethodSelection.ShowDialog(this) != DialogResult.OK) return;

            AllCopyErrors = new ObservableCollection<FileCopyError>();
            AllFileDetails.ForEach(fdet => CopyFileWithLog(fdet, frmCopyMethodSelection.CopyMode, frmCopyMethodSelection.CustomDirectoryName));

            frmCopyMethodSelection.Dispose();
            frmCopyMethodSelection = null;

            string sError = "";
            AllCopyErrors.ForEach(err => sError += err.FileDetails.FileName + " : " + err.Error + Utilities.cNewLine);
            stsCurrentStatus.Text = sNoActivity;
            stsProgress.Value = 0;

            string sMsg = "";
            if (AllFileDetails.Count == AllCopyErrors.Count)
                sMsg = "No files copied";
            else
            {
                sMsg = (AllFileDetails.Count - AllCopyErrors.Count)
                    + " file" + (AllFileDetails.Count - AllCopyErrors.Count > 0 ? "s" : "") + " copied successfully\t";
                sError = sError.Trim(Utilities.cNewLine.ToCharArray());

                if (sError.ConvertStringToLines().HasCount(10, eComparers.GreaterThan))
                    sError = sError.ConvertStringToLines().Take(10).ToArray().ConvertLinesToString()
                        + "\r\n\t+ {0} more error(s)".FormatTextFromParams(AllCopyErrors.Count - 10);
                if (AllCopyErrors.HasCount(0, eComparers.GreaterThan))
                    sMsg = sMsg.Trim('\t') + ", but with following errors: \t\r\n\r\n" + sError;
            }

            MessageBoxUtil.Show(Program.IsDebug, sMsg, "File copy summary..", MessageBoxButtons.OK, MessageBoxIcon.Information);
            Utilities.OpenPath(TargetDirectory);
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void chkAutoLoad_CheckedChanged(object sender, EventArgs e)
        {
            //mnuRefresh.Enabled = btnLoad.Enabled
            //    = Utilities.ValidatePath(PlaylistFile, ePathValidation.File); // && !chkAutoLoad.Checked
        }

        private void txtPlaylistPath_TextChanged(object sender, EventArgs e)
        {
            PlaylistFile = txtPlaylistPath.Text;
            //chkAutoLoad.Enabled = !String.IsNullOrEmpty(PlaylistFile);
        }

        private void txtTargetPath_TextChanged(object sender, EventArgs e)
        {
            TargetDirectory = txtTargetPath.Text;
        }

        private void mnuDeselectAll_Click(object sender, EventArgs e)
        {
            lstSongs.ClearSelected();
        }

        private void mnuSelectAll_Click(object sender, EventArgs e)
        {
            lstSongs.Items.Cast<FileDetails>().ForEach(fdet => lstSongs.SelectedItems.Add(fdet));
        }
        #endregion Events

        #region Methods
        private string XmlCleanup(string val)
        {
            if (val.Contains("src=\"") && hasSeqStarted)
            {
                if (val.Contains("cid=\"") && val.Contains("tid=\"")) // src + cid + tid
                {
                    if (val.IndexOf("cid=\"") < val.IndexOf("tid=\"")) // cid first
                        val = val.Replace("<media", "<media>").Replace("src=\"", "<src>").Replace("\" cid=\"", "</src><cid>").Replace("\" tid=\"", "</cid><tid>").Replace("/>", "</tid></media>");
                    else if (val.IndexOf("tid=\"") < val.IndexOf("cid=\"")) // tid first
                        val = val.Replace("<media", "<media>").Replace("src=\"", "<src>").Replace("\" tid=\"", "</src><tid>").Replace("\" cid=\"", "</tid><cid>").Replace("/>", "</cid></media>");
                }
                else if (val.Contains("cid=\"")) // src + cid
                    val = val.Replace("<media", "<media>").Replace("src=\"", "<src>").Replace("\" cid=\"", "</src><cid>").Replace("/>", "</cid></media>");
                else if (val.Contains("tid=\"")) // src + tid
                    val = val.Replace("<media", "<media>").Replace("src=\"", "<src>").Replace("\" tid=\"", "</src><tid>").Replace("/>", "</tid></media>");
                else // src
                    val = val.Replace("<media", "<media>").Replace("src=\"", "<src>").Replace("\"/>", "</src></media>");
            }

            if (!hasSeqStarted) hasSeqStarted = (val.Trim() == "<seq>");
            return val;
        }

        private string FormatFilterNames(IEnumerable<PlaylistType> playlistTypes)
        {
            string filter = "";
            foreach (PlaylistType pltype in playlistTypes)
                filter += pltype.PlaylistDescription + " (" + pltype.PlaylistExtn + ")|" + pltype.PlaylistExtn + "|";

            return filter.TrimEnd('|');
        }

        private void UpdateList(bool refreshIds)
        {
            lstSongs.DataSource = !refreshIds ? AllFileDetails :
                AllFileDetails.AddCollectionIndex<FileDetails>("FileId");
            //lstSongs.DataSource = new ObservableCollection<FileDetails>(AllFileDetails);

            lstSongs.ValueMember = "FileId";
            lstSongs.DisplayMember = "FileName";
        }

        public void EvaluateLoadButton()
        {
            mnuRefresh.Enabled = btnLoad.Enabled = PlaylistFile.ValidatePath(ePathValidation.File);
        }

        private void EvaluateDeleteButton()
        {
            mnuDelete.Enabled = btnDelete.Enabled = (lstSongs.SelectedItems.Count > 0);
            string btnText = "&Delete ~ file"
                + (lstSongs.SelectedItems.Count == 0
                    ? "(s)" : (lstSongs.SelectedItems.Count > 1 ? "s" : ""));
            mnuDelete.Text = btnDelete.Text = btnText.Replace("~", lstSongs.SelectedItems.Count > 0 ? lstSongs.SelectedItems.Count.ToString() : "").Replace("  ", "");
        }

        public void EvaluateCopyButton()
        {
            btnCopy.Enabled
                = !TargetDirectory.IsNullOrEmpty() && AllFileDetails.IsLinqIterable<FileDetails>(true);
        }

        public void CopyFileWithLog(FileDetails fdet, eCopyMode copyMode, string customName)
        {
            string targetPath = TargetDirectory + Path.DirectorySeparatorChar;
            try
            {
                switch (copyMode)
                {
                    case eCopyMode.Album:
                        targetPath += RemoveThe(fdet.Mp3Tags.Album.Trim());
                        break;
                    case eCopyMode.Artist:
                        targetPath += RemoveThe(fdet.Mp3Tags.Artist.Trim());
                        break;
                    case eCopyMode.Custom:
                        if (customName.Contains('[') && customName.Contains(']'))
                        {
                            customName = customName.Replace("[Artist]", fdet.Mp3Tags.Artist.Trim());
                            customName = customName.Replace("[Album]", fdet.Mp3Tags.Album.Trim());
                        }

                        targetPath += customName;
                        break;
                    case eCopyMode.None:
                    default:
                        targetPath += "";
                        break;
                }

                if (targetPath.ToCharArray().Skip(2).Where(ch => ":/".ToCharArray().Contains(ch)).Any())
                //if (targetPath.ToCharArray()[1] == ':' && targetPath.ToCharArray().Skip(2).Where(ch => Path.GetInvalidPathChars().Union(Path.GetInvalidFileNameChars()).Contains(ch)).Any())
                //if (!Utilities.IsPathFormatValid(targetPath))
                {
                    // @"\ / : * ? "" <> |";
                    char[] z = Path.GetInvalidPathChars(); //.Union(Path.GetInvalidFileNameChars()).Union(Path.InvalidPathChars).ToArray();
                    //z.ForEach(ch => targetPath.Replace(ch.ToString(), " - "));
                    targetPath = targetPath.TrimAndTake(2, eTrimDirection.Left) + targetPath.TrimAndLeave(2, eTrimDirection.Left).Replace(":", " - ").Replace("/", " ").Replace("?", "_");
                }

                targetPath = targetPath.Trim(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar + Path.GetFileName(fdet.FullPath);
                targetPath = targetPath.Replace("  ", " ");
                if (!Path.GetDirectoryName(targetPath).ValidatePath(ePathValidation.Directory))
                    Directory.CreateDirectory(Path.GetDirectoryName(targetPath));

                stsCurrentStatus.Text = "Copying '{0}'...".FormatTextFromParams(targetPath.Replace(TargetDirectory, "~"));
                File.Copy(fdet.FullPath, targetPath);
            }
            catch (Exception ex)
            {
                stsCurrentStatus.Text = "Error :: '{0}'...".FormatTextFromParams(targetPath.Replace(TargetDirectory, "~"));
                AllCopyErrors.Add(new FileCopyError(fdet, ex.Message.Replace(TargetDirectory, "~"))); // .Replace(targetPath, Path.GetFileName(targetPath)
            }

            stsProgress.Value = (AllFileDetails.Last() == fdet) ? 100
                : AllFileDetails.IndexOf(fdet) * 100 / AllFileDetails.Count;
            this.Refresh();
        }

        private string RemoveThe(string value)
        {
            if (value.ToLower().WordAt(0) == "the")
                value = value.TrimAndLeave(3, eTrimDirection.Left).Trim() + ", the";
            else if (value.ToLower().WordAt(0) == "a")
                value = value.TrimAndLeave(3, eTrimDirection.Left).Trim() + ", a";

            return value;
        }

        private string GetPlaylistFolder()
        {
            string pathMyMusic = Environment.SpecialFolder.MyMusic.GetFolderPath();
            string playlistDirectory = "";
            string tmp = "";

            // TEMP
            tmp = Directory.GetDirectories(pathMyMusic)._Where(dir => dir.Contains("Playlist")).FirstOrDefault();
            return tmp;



            if (new OSVersion().WindowsVersion.ToString<eWindowsVersion>().StartsWith("WinXp"))
                return "My Playlists";
            else if (new OSVersion().WindowsVersion.ToString<eWindowsVersion>().StartsWith("Win7"))
                return "Playlists";
            else if (new OSVersion().WindowsVersion.ToString<eWindowsVersion>().StartsWith("Win8"))
                throw new Exception("Playlist path not defined!"); //return "My Playlists";
            else return "";

            tmp = pathMyMusic.Trim(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar + playlistDirectory.Trim(Path.DirectorySeparatorChar);

            if (!playlistDirectory.ValidatePath(ePathValidation.Directory))
                tmp = Directory.GetDirectories(pathMyMusic)._Where(dir => dir.Contains("Playlist")).FirstOrDefault();

            return tmp;
        }
        #endregion Methods
    }
}
